from simpy import Environment

from x_pc.core import Object
from x_pc.core.resource_config import ResourceType
from x_pc.core.system_agv import SystemAGV
from x_pc.core.system_center import SystemCenter
from x_pc.core.system_context import SysDataCtx
from x_pc.core.system_cs import SystemCS
from x_pc.core.system_jx import SystemJX
from x_pc.core.system_unit import SystemUnit
from x_pc.core.system_zb import SystemZB
from x_pc.core.transport.transport_402_cs import CS
from x_pc.core.transport.transport_403_agvby import AGVBY
from x_pc.core.unit import Unit
from x_pc.exceptions import PcException
from x_pc.helpers import can_work_max_time
from x_pc.model.model import Task, TaskStatus, TaskCtx, PackType, TaskSource, DeviceType, CommandTypeCode


class WareHouse(Object):
    """
    仓库
    """
    # 栈板系统
    system_zb: SystemZB = None
    # AGV系统
    system_agv: SystemAGV = None

    # 件箱系统
    system_jx: SystemJX = None
    # 传送系统
    system_cs: SystemCS = None

    # 工作中心系统
    system_unit: SystemUnit = None
    # 工作中心 系统
    system_center: SystemCenter = None

    # 任务列表
    task_list: list[Task] = list()

    # 系统数据(初始化)
    sys_data: SysDataCtx = None

    # 系统辅助标识
    running = False

    def __init__(self, env: Environment, name: str, sys_data: SysDataCtx):
        self.task_list: list[Task] = list()
        #
        super().__init__(env, name)
        if not sys_data:
            raise PcException('[PC] 系统初始化 数据为空')
        self.sys_data = sys_data
        # 栈板系统
        self.__init_system_zb()
        self.__init_system_agv()
        # 件箱系统
        self.__init_system_jx()
        self.__init_system_cs()
        #
        self.__init_system_unit()
        self.__init_system_center()

    def __init_system_zb(self):
        """
        初始化 栈板系统
        :return:
        """
        self.system_zb = SystemZB(self.env, '栈板系统', self.sys_data)

    def __init_system_agv(self):
        """
        初始化 AGV系统
        :return:
        """
        self.system_agv = SystemAGV(self.env, 'AGV系统', self.system_zb, self, self.sys_data)

    def __init_system_jx(self):
        """
        初始化 件箱系统
        :return:
        """
        self.system_jx = SystemJX(self.env, '件箱系统', self.sys_data)

    def __init_system_cs(self):
        """
        初始化 传送系统
        :return:
        """
        self.system_cs = SystemCS(self.env, '传送系统', self.system_jx, self, self.sys_data)

    def __init_system_unit(self):
        """
        初始化 工作单元
        :return:
        """
        self.system_unit = SystemUnit(self.env, 'Unit系统', self, self.sys_data)

    def __init_system_center(self):
        """
        初始化 工作中心系统
        :return:
        """
        self.system_center = SystemCenter(self.env, '工作中心系统', self, self.sys_data)

    def push_task(self, task: Task):
        self.task_list.append(task)

    def run(self):
        """
        执行命令
        :return:
        """
        # 命令校验，是否依赖任务已完成
        while self.running:
            # 判断是否还有剩余未执行的任务
            init_tasks = [_ for _ in self.task_list if _.run_status == TaskStatus.INIT]
            # 过滤掉  未分配出货口的数据
            init_tasks = self.filter_unport_task(init_tasks)
            # 仅过滤 1.暂时需要执行的 多DC场景的 Master任务; 2.非多DC场景任务
            init_tasks = [_ for _ in init_tasks if _.is_master_dc_task or _.is_master_dc_task is None]
            if init_tasks:
                # 筛选命令执行
                task = init_tasks.pop(0)
                self.run_task(task)
                continue
            yield self.env.timeout(1)

    def filter_unport_task(self, task_list: list[Task]) -> list[Task]:
        """
        过滤掉 未分配出货口的任务
        :param task_list:
        :return:
        """
        filtered_tasks = []
        for t in task_list:
            # 当为多DC时，需要判断当前DC全部任务均可执行
            if t.is_master_dc_task:
                if all([self.valid_task(_) for _ in t.ext_group_tasks]):
                    filtered_tasks.append(t)
            elif self.valid_task(t):
                filtered_tasks.append(t)
        return filtered_tasks

    def valid_task(self, t: Task) -> bool:
        """
        校验一下 任务是否 满足执行条件
        """
        # 发货任务 且 需要用到出货口
        if t.ext_source == TaskSource.DELIVERY and t.ext_route.ext_ch_port:
            port_type = t.ext_route.ext_ch_port
            # 若未设置出货口，则先过滤掉
            available_ports = self.sys_data.factory_port[port_type][t.factory_code()]
            if not available_ports:
                t.ext_last_info = f'未找到可用的出货口({port_type})'
                return False
            # 出货口锁定判断
            has_unlocked = False
            for p in available_ports:
                pending_list = self.sys_data.get_pending_device(p.device_type(), p.device_code())
                if can_work_max_time(self.env.now, 0, pending_list):
                    has_unlocked = True
                    break
            if not has_unlocked:
                t.ext_last_info = f'未找到未锁定的出货口({port_type})'
                return False
        # 在库 (仅部分任务 走隔离逻辑)
        if t.ext_route.command_type_code in self.sys_data.inv_isolated_task:
            # 可能会指定 设备
            device_code = t.consigneekey
            # 校验下设备是否可用 (若指定了设备)
            if device_code and not self.sys_data.is_valid_device_raw(Unit.category, t.ext_zk_equ_type, device_code):
                t.set_run_status(TaskStatus.ERROR, self.sys_data)
                t.ext_last_info = f'在库 设备不可用 code: {device_code}'
                return False
            # 正常逻辑
            available_ports = self.sys_data.zk_factory_port[t.ext_zk_equ_type][t.cwmsinvtaskdetil]
            if not available_ports:
                t.ext_last_info = f'等待分配出货口({t.ext_route.command_type_code})'
                return False
            # 出货口锁定判断
            has_unlocked = False
            for p in available_ports:
                pending_list = self.sys_data.get_pending_device(p.device_type(), p.device_code())
                if can_work_max_time(self.env.now, 0, pending_list):
                    has_unlocked = True
                    break
            if not has_unlocked:
                t.ext_last_info = f'未找到未锁定的出货口({t.ext_route.command_type_code})'
                return False
        return True

    def run_task(self, task: Task):
        """
        执行单个任务
        :param task:
        :return:
        """
        # Task校验
        if not self.check_task(task):
            # 任务异常，中止处理
            task.run_status = TaskStatus.DONE
            return
        #
        task.run_status = TaskStatus.RUNNING
        # 根据任务初始位置(巷道)，判断走件箱、还是栈板
        lane = task.begin_pos.lane
        if lane in self.sys_data.get_zb_lane_data():
            if task.origin_pack_type != PackType.ZB.value:
                self.sys_data.logger.error(
                    f'[PC] ----> task: {task.task_id} 数据异常。栈板(loc={task.begin_pos.raw_loc}), 起始包规:{task.origin_pack_type}')
                return
            # 栈板系统推送任务
            self.sys_data.logger.info(
                '[PC] -------> task: {} route: {}({}): {}'.format(task.task_id, task.ext_route.id, task.ext_route.name,
                                                                  task.begin_pos))
            self.system_zb.push_task(TaskCtx(task))
        elif lane in self.sys_data.get_jx_lane_data():
            if task.origin_pack_type == PackType.ZB.value:
                self.sys_data.logger.error(
                    f'[PC] ----> task: {task.task_id} 数据异常。件箱(loc={task.begin_pos.raw_loc}), 起始包规:{task.origin_pack_type}')
                return  # 件箱系统推送任务
            self.sys_data.logger.info(
                '[PC] -------> task: {} {}({}): {}'.format(task.task_id, task.ext_route.id, task.ext_route.name,
                                                          task.begin_pos))
            self.system_jx.push_task(TaskCtx(task))
        else:
            self.sys_data.logger.error(
                f'[PC] 暂未支持任务类型(根据LOC无法识别) task: {task.task_id}, begin_pos: {task.begin_pos}')

    def distribute_task(self, task_ctx: TaskCtx):
        """
        分发任务
        :param task_ctx:
        :return:
        """
        task = task_ctx.task
        process = task.process
        resource_type = process.ext_resource_type
        equ_type = process.ext_step.ext_equ_type
        # 按资源类型进行分发任务
        if ResourceType.UNIT == resource_type:
            self.system_unit.push_task(task_ctx)
        elif ResourceType.CENTER == resource_type:
            self.system_center.push_task(task_ctx)
        elif ResourceType.TRANSPORT == resource_type:
            # # 仅Center-GTP存在多设备类型情况
            _equ_type = equ_type[0]
            # 当前Transport 只会涉及 agvby 和 cs; 另外一个hc只会在栈板出入库时使用到
            if AGVBY.device_type() == _equ_type:
                self.system_agv.push_task(task_ctx)
            elif CS.device_type() == _equ_type:
                self.system_cs.push_task(task_ctx)
        else:
            msg = f'[PC] 暂未支持任务类型: {resource_type}, process: {process}'
            self.sys_data.logger.warning(msg)

    @staticmethod
    def check_task(task: Task):
        if not task.begin_pos or not task.process_list:
            return False
        #
        task.process_list.sort(key=lambda _: _.order)
        first_process = task.process_list[0]
        if not first_process.begin_pos:
            first_process.begin_pos = task.begin_pos
        return True

    def start(self):
        msg = f'[PC] {self.name} 开始启动'
        self.sys_data.logger.info(msg)
        self.running = True
        # 栈板巷道 系统
        self.system_zb.start()
        # AGV系统
        self.system_agv.start()
        # 件箱系统
        self.system_jx.start()
        # 传送系统
        self.system_cs.start()
        # 工作单元
        self.system_unit.start()
        # 工作中心
        self.system_center.start()

    def stop(self):
        self.running = False
        # 栈板巷道 系统
        self.system_zb.stop()
        # AGV系统
        self.system_agv.stop()
        # 件箱系统
        self.system_jx.stop()
        # 传送系统
        self.system_cs.stop()
        # 工作单元
        self.system_unit.stop()
        # 工作中心
        self.system_center.stop()
        #
        msg = f'[PC] {self.name} 停止运动'
        self.sys_data.logger.info(msg)
